# coding: utf-8
# author: t_wolf

import framework.dependence.web as web
from framework.dependence.web.session import Session, Store, DBStore
from framework.dependence.web.webapi import HTTPError
from framework.configure import *


class DbUtil:
    def __init__(self, _db_configs, _sql=None):
        self.sql = None
        self.db_obj = None
        self.init_sql = {} if _sql is None else _sql
        self.init_db_configs = _db_configs

        self.init_db_object()
        self.sql_generator()

    def init_db_object(self):
        if 'type' in self.init_db_configs:
            self.db_obj = DbUtil.auto_connect(**self.init_db_configs)
        else:
            for _key, _value in self.init_db_configs.items():
                if _key == 'default':
                    self.db_obj = DbUtil.auto_connect(**_value)
                else:
                    setattr(self, _key, DbUtil(_value, self.init_sql))

    def sql_generator(self):
        if isinstance(self.init_sql, storage):
            self.sql = self.init_sql
        else:
            self.sql = storager(
                get_object_from_module(self.init_sql, dict)
            )

    @staticmethod
    def auto_connect(**kwargs):
        return web.database(
            dbn=kwargs.get('type'), host=kwargs.get('host', None), port=kwargs.get('port', None),
            user=kwargs.get('username', None), pw=kwargs.get('password', None), db=kwargs.get('database', None),
            # pooling=False
        )

    @staticmethod
    def roll_back_on_exception(db_conn):
        def wrap_func(func):
            def wrapper(*args, **kwargs):
                trans = db_conn.transaction()
                try:
                    res = func(*args, **kwargs)
                except Exception:
                    trans.rollback()
                    raise
                else:
                    trans.commit()
                return res

            return wrapper

        return wrap_func

    @staticmethod
    def redis_connector(host, port, db):
        web.config.debug_sql and logger.dev("Arguments: host[%s], port[%s], db[%s]" % (host, port, db))
        pool = redis.ConnectionPool(host=host, port=port, db=db)
        r = redis.Redis(connection_pool=pool)
        try:
            r.dbsize()
        except Exception, e:
            logger.critical("Testing Redis Connection Failed, Cause By %s" % e)
        else:
            return r

    def execute(self, sql_sign, var=None):
        exec_sql = None
        try:
            web.config.debug_sql and logger.debug("Current execute sql [%s], vars=[%s]" % (sql_sign, var))
            exec_sql = self.sql.get_value_from_dot_path(sql_sign)
            _res = self.db_obj.query(exec_sql, vars=var)
            if hasattr(_res, '__iter__'):
                _res = list(_res)

            return _res

        except RuntimeError:
            logger.error("Wrong Arguments [%s] for database sql." % sql_sign)
            raise web.internalerror("Wrong Arguments for database sql.")
        except Exception, e:
            logger.error("At Execute SQL: [%s], Cause By: %s" % (exec_sql, e))

    findall = execute

    def find(self, sql_sign, var=None):
        try:
            _res = self.execute(sql_sign, var)
            if isinstance(_res, list):
                return _res[0]
            else:
                return _res

        except IndexError:
            return None

    def query(self, sql_str, var=None):
        exec_sql = sql_str
        try:
            _res = self.db_obj.query(exec_sql, vars=var)
            if hasattr(_res, '__iter__'):
                _res = list(_res)

            return _res

        except Exception, e:
            logger.error("At Execute SQL: [%s], Cause By: %s" % (exec_sql, e))

    def get(self, sql_str, var=None):
        try:
            _res = self.query(sql_str, var)
            if isinstance(_res, list):
                return _res[0]
            else:
                return _res

        except IndexError:
            return None

    def select(self, *args, **kwargs):
        return self.db_obj.select(*args, **kwargs)

    def update(self, *args, **kwargs):
        return self.db_obj.update(*args, **kwargs)

    def insert(self, *args, **kwargs):
        return self.db_obj.insert(*args, **kwargs)

    def delete(self, *args, **kwargs):
        return self.db_obj.delete(*args, **kwargs)

    def exists(self, sql, var=None, sql_type="SIGN"):
        if sql_type == "SIGN":
            _res = self.execute(sql, var)
        else:
            _res = self.query(sql, var)

        return bool(_res)
